package com.company.nuwa.redis.support.limit;

import com.company.nuwa.common.exception.BizException;
import com.company.nuwa.redis.utils.SpelExpressionUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

/**
 * <p>redis rate Aspect</p >
 *
 * @author Nikola Tesla
 * @version 1.0
 * @date 2021/1/12 14:04
 */
@Aspect
@Slf4j
public class RedisRateLimiterAspect {

    private final RedisRateLimiter redisRateLimiter;

    public RedisRateLimiterAspect(RedisRateLimiter redisRateLimiter) {
        this.redisRateLimiter = redisRateLimiter;
    }

    @Pointcut("@annotation(com.company.nuwa.redis.support.limit.RedisLimit)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        RedisLimit limit = methodSignature.getMethod().getDeclaredAnnotation(RedisLimit.class);

        String key = SpelExpressionUtil.parseExpression(joinPoint, limit.key());

        log.info("signature={} distribute limit key={}, rate={}, rate interval={}", joinPoint.getSignature().toString(), key, limit.rate(), limit.rateInterval());

        return redisRateLimiter.tryAcquire(key, limit.rate(), limit.rateInterval(),
                () -> {
                    try {
                        return joinPoint.proceed(joinPoint.getArgs());
                    } catch (Throwable throwable) {
                        log.error("", throwable);
                        if (throwable instanceof Exception) {
                            throw (Exception) throwable;
                        }
                        throw new BizException(-1, "SERVICE_BUSY");
                    }
                },
                () -> {
                    throw new BizException(-1, "REQUEST_BUSY");
                }
        );


    }


}
